{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# nuScenes Map Expansion Tutorial"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is the tutorial for the nuScenes map expansion. In particular, the `NuScenesMap` data class. \n",
    "\n",
    "This tutorial will go through the description of each layers, how we retrieve and query a certain record within the map layers, render methods, and advanced data exploration\n",
    "\n",
    "In database terms, layers are basically tables of the map database in which we assign arbitrary parts of the maps with informative labels such as `traffic_light`, `stop_line`, `walkway`, etc. Refer to the discussion on layers for more details."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "To install the map expansion, please download the files from https://www.nuscenes.org/download and copy the files into your nuScenes map folder, e.g. `/data/sets/nuscenes/maps`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Initialization\n",
    "\n",
    "We will be working with the `singapore-onenorth` map. The `NuScenesMap` can be initialized as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import tqdm\n",
    "import numpy as np\n",
    "\n",
    "from nuscenes.map_expansion.map_api import NuScenesMap\n",
    "from nuscenes.map_expansion import arcline_path_utils\n",
    "from nuscenes.map_expansion.bitmap import BitMap\n",
    "\n",
    "nusc_map = NuScenesMap(dataroot='/data/sets/nuscenes', map_name='singapore-onenorth')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualization"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we go into the details, let's visualize the map."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rendering multiple layers\n",
    "\n",
    "The `NuScenesMap` class makes it possible to render multiple map layers on a matplotlib figure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_layers(nusc_map.non_geometric_layers, figsize=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rendering the lidar basemap\n",
    "**New:** We can render the HD lidar basemap used for localization. The basemap is a bitmap image that can be underlaid for most functions (`render_centerlines`, `render_egoposes_on_fancy_map`, `render_layers`, `render_map_patch`, `render_next_roads`, `render_record`). The same `BitMap` class can also be used to render the semantic prior (drivable surface + sidewalk) from the original nuScenes release. Note that in this visualization we only show the `lane` annotations for better visibility."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bitmap = BitMap(nusc_map.dataroot, nusc_map.map_name, 'basemap')\n",
    "fig, ax = nusc_map.render_layers(['lane'], figsize=1, bitmap=bitmap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rendering a particular record of the map layer\n",
    "\n",
    "We can render a record, which will show its global and local view"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('stop_line', nusc_map.stop_line[14]['token'], other_layers=[], bitmap=bitmap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rendering binary map mask layers\n",
    "\n",
    "The `NuScenesMap` class makes it possible to convert multiple map layers into binary mask and render on a Matplotlib figure. First let's call `get_map_mask` to look at the raw data of two layers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "patch_box = (300, 1700, 100, 100)\n",
    "patch_angle = 0  # Default orientation where North is up\n",
    "layer_names = ['drivable_area', 'walkway']\n",
    "canvas_size = (1000, 1000)\n",
    "map_mask = nusc_map.get_map_mask(patch_box, patch_angle, layer_names, canvas_size)\n",
    "map_mask[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we directly visualize the map mask retrieved above using `render_map_mask`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "figsize = (12, 4)\n",
    "fig, ax = nusc_map.render_map_mask(patch_box, patch_angle, layer_names, canvas_size, figsize=figsize, n_row=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also render the same map rotated by 45 degrees clockwise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_map_mask(patch_box, 45, layer_names, canvas_size, figsize=figsize, n_row=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rendering map layers on top of camera images\n",
    "Let us take a nuScenes camera image and overlay the relevant map layers.\n",
    "Note that the projections are not perfect if the ground is uneven as the localization is 2d."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Init nuScenes. Requires the dataset to be stored on disk.\n",
    "from nuscenes.nuscenes import NuScenes\n",
    "nusc = NuScenes(version='v1.0-mini', dataroot='/data/sets/nuscenes', verbose=False)\n",
    "\n",
    "# Pick a sample and render the front camera image.\n",
    "sample_token = nusc.sample[9]['token']\n",
    "layer_names = ['road_segment', 'lane', 'ped_crossing', 'walkway', 'stop_line', 'carpark_area']\n",
    "camera_channel = 'CAM_FRONT'\n",
    "nusc_map.render_map_in_image(nusc, sample_token, layer_names=layer_names, camera_channel=camera_channel)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rendering ego poses on the map\n",
    "We can also plot the ego poses onto the map. This requires us to load up the `NuScenes` class, which can take some time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Init NuScenes. Requires the dataset to be stored on disk.\n",
    "from nuscenes.nuscenes import NuScenes\n",
    "nusc = NuScenes(version='v1.0-mini', dataroot='/data/sets/nuscenes', verbose=False)\n",
    "\n",
    "# Render ego poses.\n",
    "nusc_map_bos = NuScenesMap(dataroot='/data/sets/nuscenes', map_name='boston-seaport')\n",
    "ego_poses = nusc_map_bos.render_egoposes_on_fancy_map(nusc, scene_tokens=[nusc.scene[1]['token']], verbose=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Navigation\n",
    "We also provide functions for navigation around the road network. For this purpose, the road layers `lane`, `road_block` and `road_segment` are especially useful (see definitions below). The `get_next_roads(x, y)` function looks at the road layer at a particular point. It then retrieves the next road object in the direction of the `lane` or `road_block`. As `road_segments` do not have a direction (e.g. intersections), we return all possible next roads."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 873\n",
    "y = 1286\n",
    "print('Road objects on selected point:', nusc_map.layers_on_point(x, y), '\\n')\n",
    "print('Next road objects:', nusc_map.get_next_roads(x, y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also visualize the next roads using the `render_next_roads(x, y)` function. We see that there are 3 adjacent roads to the intersection specified by (x, y)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.render_next_roads(x, y, figsize=1, bitmap=bitmap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Working with Lanes\n",
    "For the prediction challenge we added connectivity information to the map expansion (v1.2) to efficiently query which lane is connected to which other lanes. Below we render the lane and lane_connector objects. The lanes and lane_connectors are defined by parametric curves. The `resolution_meters` parameter specifies the discretization resolution of the curve. If we set it to a high value (e.g. 100), the curves will appear as straight lines. We recommend setting this value to 1m or less."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.render_centerlines(resolution_meters=0.5, figsize=1, bitmap=bitmap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get the closest lane to a location, use the `get_closest_lane` method. To see the internal data representation of the lane, use the `get_lane_record` method. \n",
    "You can also explore the connectivity of the lanes, with the `get_outgoing_lanes` and `get_incoming_lane` methods."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, y, yaw = 395, 1095, 0\n",
    "closest_lane = nusc_map.get_closest_lane(x, y, radius=2)\n",
    "closest_lane"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lane_record = nusc_map.get_arcline_path(closest_lane)\n",
    "lane_record"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.get_incoming_lane_ids(closest_lane)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.get_outgoing_lane_ids(closest_lane)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To help manipulate the lanes, we've added an `arcline_path_utils` module. For example, something you might want to do is discretize a lane into a sequence of poses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "poses = arcline_path_utils.discretize_lane(lane_record, resolution_meters=1)\n",
    "poses"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Given a query pose, you can also find the closest pose on a lane."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "closest_pose_on_lane, distance_along_lane = arcline_path_utils.project_pose_to_lane((x, y, yaw), lane_record)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(x, y, yaw)\n",
    "closest_pose_on_lane"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Meters\n",
    "distance_along_lane"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To find the entire length of the lane, you can use the `length_of_lane` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "arcline_path_utils.length_of_lane(lane_record)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also compute the curvature of a lane at a given length along the lane."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 0 means it is a straight lane\n",
    "arcline_path_utils.get_curvature_at_distance_along_lane(distance_along_lane, lane_record)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Exploration"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's render a particular patch on the map:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_patch = (300, 1000, 500, 1200)\n",
    "fig, ax = nusc_map.render_map_patch(my_patch, nusc_map.non_geometric_layers, figsize=(10, 10), bitmap=bitmap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A lot of layers can be seen in this patch. Lets retrieve all map records that are in this patch.\n",
    "\n",
    "- The option `within` will return all non geometric records that ***are within*** the map patch\n",
    "- The option `intersect` will return all non geometric records that ***intersect*** the map patch\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "records_within_patch = nusc_map.get_records_in_patch(my_patch, nusc_map.non_geometric_layers, mode='within')\n",
    "records_intersect_patch = nusc_map.get_records_in_patch(my_patch, nusc_map.non_geometric_layers, mode='intersect')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since there are a lot of records, we focus only on the layer `road_segment`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "layer = 'road_segment'\n",
    "print('Found %d records of %s (within).' % (len(records_within_patch[layer]), layer))\n",
    "print('Found %d records of %s (intersect).' % (len(records_intersect_patch[layer]), layer))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that using the option `intersect` typically returns more records than `within`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Looking at the above plot. Point `(390, 1100)` seems to be on a stop line. Lets verify that."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_point = (390, 1100)\n",
    "layers = nusc_map.layers_on_point(my_point[0], my_point[1])\n",
    "assert len(layers['stop_line']) > 0, 'Error: No stop line found!'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Indeed, we see a `stop_line` record.\n",
    "\n",
    "To directly check for `stop_line` records, we run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.record_on_point(my_point[0], my_point[1], 'stop_line')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look at the bounds/extremities of that record"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.get_bounds('stop_line', 'ac0a935f-99af-4dd4-95e3-71c92a5e58b1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Layers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us look more closely at the different map layers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.layer_names"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Our map database consists of multiple **layers**. Where each layer is made up of **records**. Each record will have a token identifier.\n",
    "\n",
    "We see how our map layers are divided into two types of layers. One set of layer belong to the `geometric_layers` group, another set of layers belongs to the `non_geometric_layers` group.  \n",
    "1. `geometric_layers` define geometric entities in the map:\n",
    "    - Nodes (2d points) are the basis for all geometric layers.\n",
    "    - Lines consist of two or more nodes. Formally, one `Line` record can consist of more than one line segment.\n",
    "    - Polygons consist of three or more nodes. A polygon can have holes, thus distorting its formal definition. Holes are defined as a sequence of nodes that forms the perimeter of the polygonal hole.\n",
    "    \n",
    "    \n",
    "2. `non_geometric_layers` represent physical entities in the map. They can have more than one geometric representation (such as `drivable_areas`), but must be strictly of one type (e.g. `road_segment`, `lane_divider`)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Geometric layers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.geometric_layers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### a. Node\n",
    "The most primitive geometric record in our map database. This is the only layer that explicitly contains spatial coordinates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_node = nusc_map.node[0]\n",
    "sample_node"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### b. Line\n",
    "\n",
    "Defines a line sequence of one or more lines and therefore consists of two or more nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_line = nusc_map.line[2]\n",
    "sample_line"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### c. Polygon \n",
    "Defines a polygon which may contain holes.\n",
    "\n",
    "Every polygon record comprises of a list of exterior nodes, and zero or more list(s) of nodes that constitute (zero or more) holes.\n",
    "\n",
    "Let's look at one polygon record:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_polygon = nusc_map.polygon[3]\n",
    "sample_polygon.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_polygon['exterior_node_tokens'][:10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_holes = sample_polygon['holes'][0]\n",
    "sample_holes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Non geometric layers\n",
    "\n",
    "Every non-geometric layer is associated with at least one geometric object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nusc_map.non_geometric_layers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### a. Drivable Area\n",
    "Drivable area is defined as the area where the car can drive, without consideration for driving directions or legal restrictions. This is the only layer in which the record can be represented by more than one geometric entity.\n",
    "*Note: On some machines this polygon renders incorrectly as a filled black rectangle.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_drivable_area = nusc_map.drivable_area[0]\n",
    "sample_drivable_area"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('drivable_area', sample_drivable_area['token'], other_layers=[])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### b. Road Segment\n",
    "\n",
    "A segment of road on a drivable area. It has an `is_intersection` flag which denotes whether a particular road segment is an intersection.\n",
    "\n",
    "It may or may not have an association with a `drivable area` record from its `drivable_area_token` field."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "sample_road_segment = nusc_map.road_segment[600]\n",
    "sample_road_segment"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As observed, for all non geometric objects except `drivable_area`, we provide a shortcut to its `nodes`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take a look at a `road_segment` record with `is_intersection == True`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_intersection_road_segment = nusc_map.road_segment[3]\n",
    "sample_intersection_road_segment"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we render this road segment we can see that it is indeed an intersection:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('road_segment', sample_intersection_road_segment['token'], other_layers=[])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### c. Road Block\n",
    "Road blocks are blocks of a road that have the same traffic direction. Multiple road blocks are grouped in a road segment.\n",
    "\n",
    "Within a road block, the number of lanes is consistent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_road_block = nusc_map.road_block[0]\n",
    "sample_road_block"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Every road block has a `from_edge_line_token` and `to_edge_line_token` that denotes its traffic direction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('road_block', sample_road_block['token'], other_layers=[])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### d. Lanes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lanes are parts of the road where vehicles drive in a single direction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "sample_lane_record = nusc_map.lane[600]\n",
    "sample_lane_record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aside from the token and the geometric representation, a lane has several fields:\n",
    "- `lane_type` denotes whether cars or bikes are allowed to navigate through that lane.\n",
    "- `from_edge_line_token` and `to_edge_line_token` denotes their traffic direction.\n",
    "- `left_lane_divider_segments` and `right_lane_divider_segment` denotes their lane dividers.\n",
    "- `left_lane_divider_segment_nodes` and `right_lane_divider_segment_nodes` denotes the nodes that makes up the lane dividers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('lane', sample_lane_record['token'], other_layers=[])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### e. Pedestrian Crossing\n",
    "Pedestrian crossings are regions where pedestrians can legally cross the road, typically highlighted by white markings. Each pedestrian crossing record has to be on a road segment. It has the `road_segment_token` field which denotes the `road_segment` record it is associated with."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_ped_crossing_record = nusc_map.ped_crossing[0]\n",
    "sample_ped_crossing_record"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('ped_crossing', sample_ped_crossing_record['token'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### f. Walkway\n",
    "A walkway or sidewalk is the typically elevated area next to a road where pedestrians are protected from vehicles on the road."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_walkway_record = nusc_map.walkway[0]\n",
    "sample_walkway_record"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('walkway', sample_walkway_record['token'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### g. Stop Line\n",
    "The physical world's stop line, even though the name implies that it should possess a `line` geometric representation, in reality its physical representation is an **area where the ego vehicle must stop.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_stop_line_record = nusc_map.stop_line[1]\n",
    "sample_stop_line_record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It has several attributes:\n",
    "- `stop_line_type`, the type of the stop line, this represents the reasons why the ego vehicle would stop         \n",
    "- `ped_crossing_tokens` denotes the association information if the `stop_line_type` is `PED_CROSSING`.\n",
    "- `traffic_light_tokens` denotes the association information if the `stop_line_type` is `TRAFFIC_LIGHT`.\n",
    "- `road_block_token` denotes the association information to a `road_block`, can be empty by default. \n",
    "- `cues` field contains the reason on why this this record is a `stop_line`. An area can be a stop line due to multiple reasons:\n",
    "    - Cues for `stop_line_type` of \"PED_CROSSING\" or \"TURN_STOP\" are `ped_crossing` records.\n",
    "    - Cues for `stop_line_type` of TRAFFIC_LIGHT\" are `traffic_light` records.\n",
    "    - No cues for `stop_line_type` of \"STOP_SIGN\" or \"YIELD\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('stop_line', sample_stop_line_record['token'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### h. Carpark Area\n",
    "A car park or parking lot area."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_carpark_area_record = nusc_map.carpark_area[1]\n",
    "sample_carpark_area_record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It has several attributes:\n",
    "- `orientation` denotes the direction of parked cars in radians.\n",
    "- `road_block_token` denotes the association information to a `road_block`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('carpark_area', sample_carpark_area_record['token'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### i. Road Divider\n",
    "A divider that separates one road block from another."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_road_divider_record = nusc_map.road_divider[0]\n",
    "sample_road_divider_record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`road_segment_token` saves the association information to a `road_segment`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('road_divider', sample_road_divider_record['token'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### j. Lane Divider\n",
    "A lane divider comes between lanes that point in the same traffic direction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_lane_divider_record = nusc_map.lane_divider[0]\n",
    "sample_lane_divider_record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `lane_divider_segments` field consist of different `node`s and their respective `segment_type`s which denotes their physical appearance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, ax = nusc_map.render_record('lane_divider', sample_lane_divider_record['token'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### k. Traffic Light\n",
    "A physical world's traffic light."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_traffic_light_record = nusc_map.traffic_light[0]\n",
    "sample_traffic_light_record"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It has several attributes:\n",
    "1. `traffic_light_type` denotes whether the traffic light is oriented horizontally or vertically.\n",
    "2. `from_road_block_tokens` denotes from which road block the traffic light guides.\n",
    "3. `items` are the bulbs for that traffic light.\n",
    "4. `pose` denotes the pose of the traffic light."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's examine the `items` field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_traffic_light_record['items']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As mentioned, every entry in the `items` field is a traffic light bulb. It has the `color` information, the `shape` information, `rel_pos` which is the relative position, and the `to_road_block_tokens` that denotes to which road blocks the traffic light bulb is guiding."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
